package controllers

import (
	"bytes"
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"encoding/pem"
	"errors"
	"flag"
	"io/ioutil"
	"math"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/golang/protobuf/proto"
	"github.com/gorilla/websocket"
	"assist-server/models"
	"game-server/msg"
)

const (
	base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
)

var coder = base64.NewEncoding(base64Table)

func base64Encode(src []byte) []byte {
	return []byte(coder.EncodeToString(src))
}

func base64Decode(src []byte) ([]byte, error) {
	return coder.DecodeString(string(src))
}

var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI384Q78U/MFMsFy3OHVuGKgw1+VCb50HRaAT+XiZWBvbULBwmEK3fqvoaRbTUHUA8bCgqBaZX7ant2NRFgIYZtp/WaX/yXD38KA15dWpfWmDwzrCZmtdxeQMRGgNwSkZup129kFa4QVMlxHWfqiYmOB2Z16/E+Cku1xpBcysX4QIDAQAB
-----END PUBLIC KEY-----
`)

var addr = flag.String("addr", beego.AppConfig.String("socket"), "http service address")
var conn *websocket.Conn // websocket链接
var connStatus bool      // 链接状态
var retry int = 1

// Operations about Users
type RechargeController struct {
	beego.Controller
}

func init() {
	initConn()
}

// 初始化websocket链接
func initConn() {
	u := url.URL{Scheme: "ws", Host: *addr, Path: "/"}

	connStatus = true
	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		beego.Info("dial:", err)
		connStatus = false
		retryConn()
	} else {
		conn = c
		retry = 1
		//defer conn.Close()

		beego.Info("connecting to ", u.String())
	}
}

// 重连机制
func retryConn() {
	beego.Info("retry = ", retry)
	if retry < 5 {
		t := time.Duration(retry*10 + 5)
		time.Sleep(t * time.Second)
		retry++
		initConn()
	}
}

// @Title CreateOrder
// @Description create order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router / [post]
func (r *RechargeController) Post() {
	var pay models.Pay
	var recharge models.Recharge
	var order models.TLogOrder

	json.Unmarshal(r.Ctx.Input.RequestBody, &recharge)
	beego.Info("request = ", recharge)

	// 获取订单号，时间
	orderId, t := models.CreateOrder(recharge)

	// 查询商品名称、价格
	//var item interface{}
	if recharge.Channel == "android" {
		item := models.GetItem(recharge.RechargeID)

		order.Money = item.Money
		pay.Name = item.Name
		pay.Money = int(item.Money)
	} else {
		item := models.GetItemI(recharge.RechargeID)

		order.Money = item.Money
		pay.Name = item.Name
		pay.Money = int(item.Money)
	}

	base, _ := beego.AppConfig.Int("baseNum")

	// 订单信息
	//order.Money = item.Money
	order.RechargeId = uint(recharge.RechargeID)
	order.OrderId = orderId
	order.Channel = recharge.Channel
	order.Uin = uint(recharge.UserId - base)
	order.CreateTime = t
	models.PayInfo(order)

	// 支付信息
	//pay.Name = item.Name
	//pay.Money = int(item.Money)
	pay.OrderId = orderId
	r.Data["json"] = pay
	r.ServeJSON()
}

// @Title CheckOrder
// @Description check order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /callBack [post]
func (r *RechargeController) CallBack() {
	var backData models.CheckInfo
	json.Unmarshal(r.Ctx.Input.RequestBody, &backData)
	beego.Info("data = ", backData)

	order := models.CheckOrder(backData.Cporder, 0)
	if order.Uin > 0 {
		r.Data["json"] = "ok"
		// 链接失败不发送，等待链接成功
		if connStatus {
			send(order)
		}
	} else {
		r.Data["json"] = "fail"
	}
	r.ServeJSON()
}

// @Title CheckOrder
// @Description check order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /callBack/ios [post]
func (r *RechargeController) Ios() {
	var pBody map[string]interface{}
	json.Unmarshal(r.Ctx.Input.RequestBody, &pBody)
	beego.Info("body = ", pBody)

	if pBody["sign"] != nil && pBody["orderId"] != nil {
		tmp := `{"receipt-data":"` + pBody["sign"].(string) + `"}`
		req := bytes.NewBuffer([]byte(tmp))

		body_type := "application/json"
		resp, _ := http.Post("https://sandbox.iTunes.Apple.com/verifyReceipt", body_type, req)
		defer resp.Body.Close()
		body, _ := ioutil.ReadAll(resp.Body)
		//fmt.Printf("body = %v",body)
		var aReturn map[string]interface{}
		json.Unmarshal(body, &aReturn)
		beego.Info("aReturn = ", aReturn)

		order := models.CheckOrder(pBody["orderId"].(string), 0)
		if order.Uin > 0 {
			r.Data["json"] = "购买成功"
			// 链接失败不发送，等待链接成功
			if connStatus {
				send(order)
			}
		}
		r.ServeJSON()
	}
}

// @Title CheckOrder
// @Description check order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /callBack/aibei [post]
func (r *RechargeController) Aibei() {
	var param string
	pBody := string(r.Ctx.Input.RequestBody)
	beego.Info("body = ", pBody)

	// transdata=%7B%22appid%22%3A%223015805262%22%2C%22appuserid%22%3A%2210001%22%2C%22cporderid%22%3A%2272c79e0cddab7e2a74afe1c8a62e2cf3%22%2C%22cpprivate%22%3A%22%22%2C%22currency%22%3A%22RMB%22%2C%22feetype%22%3A2%2C%22money%22%3A0.10%2C%22paytype%22%3A4%2C%22result%22%3A0%2C%22transid%22%3A%2232471710171919262518%22%2C%22transtime%22%3A%222017-10-17+19%3A21%3A24%22%2C%22transtype%22%3A0%2C%22waresid%22%3A1%7D&sign=MKAyDm2PJFtAR8Lk0%2B%2Fx7bR3RcR5DnFR%2FJEEk%2BYiRimXUBfBgchlgkem%2Fy7TaAi2DDqNu6OrzkohiXwcPtVghHUKAfVLkBlb%2BMMTIpoUJxlgVE6i3qze4HdBlZS7MSFEsd%2FFPOcTydYVeEONGcmXTstjBrMRa9hEpIyPSHkE7xw%
	// transdata={"appid":"3015805262","appuserid":"10001","cporderid":"72c79e0cddab7e2a74afe1c8a62e2cf3","cpprivate":"","currency":"RMB","feetype":2,"money":0.10,"paytype":4,"result":0,"tr ansid":"32471710171919262518","transtime":"2017-10-17 19:21:24","transtype":0,"waresid":1}&sign=MKAyDm2PJFtAR8Lk0+/x7bR3RcR5DnFR/JEEk+YiRimXUBfBgchlgkem/y7TaAi2DDqNu6OrzkohiXwcPtVghHUKAfVLkBlb+MMTIpoUJxlgVE6i3qze4HdBlZS7MSFEsd/FPOcTyd YVeEONGcmXTstjBrMRa9hEpIyPSHkE7xw%
	// urldecode数据
	if strings.Contains(pBody, "%22") {
		p, err := url.QueryUnescape(pBody)
		if err == nil {
			param = p
		}
		beego.Info("urldecode = ", param)
	}

	params := strings.Split(param, "&")
	transdata := strings.Split(params[0], "=")[1]
	sign := strings.Split(params[1], "=")[1]
	beego.Info("transdata = ", transdata)
	beego.Info("sign = ", sign)

	// 验证数据合法性
	err := Rsacrypt(transdata, sign+"=", crypto.MD5)
	if err != nil {
		r.Data["json"] = "FAIL"
	} else {
		var tran map[string]interface{}
		json.Unmarshal([]byte(transdata), &tran)

		if tran["result"].(float64) == 0 {
			order := models.CheckOrder(tran["cporderid"].(string), uint(tran["money"].(float64)*100))
			if order.Uin > 0 {
				beego.Info("bb = ", tran["money"])
				r.Data["json"] = "SUCCESS"
				// 链接失败不发送，等待链接成功
				if connStatus {
					send(order)
				}
			} else {
				r.Data["json"] = "FAIL"
			}
			beego.Info("bb = ", tran["money"])
			beego.Info("bb = ", tran["cporderid"])
		} else {
			r.Data["json"] = "FAIL"
		}
	}
	r.ServeJSON()
}

// 签名验证
func Rsacrypt(src, sign string, hash crypto.Hash) error {
	h := hash.New()
	h.Write([]byte(src))
	hashed := h.Sum(nil)

	signature, _ := base64Decode([]byte(sign))

	block, _ := pem.Decode(publicKey)
	if block == nil {
		return errors.New("public key error")
	}
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err
	}
	pub := pubInterface.(*rsa.PublicKey)
	return rsa.VerifyPKCS1v15(pub, hash, hashed, signature)
}

// @Title CheckOrder
// @Description check order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /list [post]
func (r *RechargeController) List() {
	var pItem models.PItem
	json.Unmarshal(r.Ctx.Input.RequestBody, &pItem)
	beego.Info("data = ", pItem)

	//items := models.GetItemList(pItem.Channel)
	if pItem.Channel == "android" {
		r.Data["json"] = models.GetItemAndroid()
	} else {
		r.Data["json"] = models.GetItemIos()
	}
	//r.Data["json"] = items
	r.ServeJSON()
}

// @Title CreateOrder
// @Description create order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /userInfo [post]
func (r *RechargeController) UserInfo() {
	var user map[string]interface{}
	err := json.Unmarshal(r.Ctx.Input.RequestBody, &user)
	beego.Info("json data = ", user)

	if err == nil {
		base, _ := beego.AppConfig.Int("baseNum")
		userInfo := models.GetUserInfo(uint(user["gameId"].(float64)) - uint(base))
		if userInfo.Uin > 0 {
			data := models.PlayerInfo{
				Code:   200,
				GameId: userInfo.Uin + uint(base),
				Name:   userInfo.Nick,
			}
			r.Data["json"] = data
		} else {
			data := models.PlayerInfo{
				Code: 500,
			}
			r.Data["json"] = data
		}
	} else {
		data := models.PlayerInfo{
			Code: 500,
			Name: "",
		}
		r.Data["json"] = data
	}

	r.ServeJSON()
}

// @Title CreateOrder
// @Description create order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /agency [post]
func (r *RechargeController) Agency() {
	var sCard models.SendCard
	var order models.TLogOrder
	var msgInfo models.MsgInfo

	json.Unmarshal(r.Ctx.Input.RequestBody, &sCard)
	beego.Info("request = ", sCard)

	recharge := models.Recharge{
		UserId:     sCard.GameId,
		Channel:    sCard.Channel,
		RechargeID: sCard.CardNum,
	}

	// 获取订单号，时间
	orderId, t := models.CreateOrder(recharge)

	base, _ := beego.AppConfig.Int("baseNum")

	// 订单信息
	order.Money = 0
	order.Status = 1
	order.RechargeId = uint(recharge.RechargeID)
	order.OrderId = orderId
	order.Channel = recharge.Channel
	order.Uin = uint(recharge.UserId - base)
	order.CreateTime = t
	err := models.PayInfo(order)
	if err != nil {
		// 发送房卡状态
		msgInfo.Code = 500
		msgInfo.Msg = "fail"
	} else {
		//models.UpdateCards(order.Uin, order.RechargeId)
		// 链接失败不发送，等待链接成功
		if connStatus {
			s := send(order)
			if s {
				beego.Info("test")
				msgInfo.Code = 200
				msgInfo.Msg = "ok"
			} else {
				msgInfo.Code = 500
				msgInfo.Msg = "fail"
			}
		}
	}

	r.Data["json"] = msgInfo
	r.ServeJSON()
}

func send(order models.TLogOrder) bool {
	if order.Uin > 0 {
		userId := order.Uin
		base, _ := beego.AppConfig.Int("baseNum")
		Goods := &msg.MsgSendGoods{
			UserId:  int32(userId) + int32(base),
			OrderId: order.OrderId,
		}

		data, _ := proto.Marshal(Goods)

		return webSocket(data, 10010)
	} else {
		beego.Info("充值成功，已发放过物品。")
		return false
	}
}

// @Title CreateOrder
// @Description create order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /room [post]
func (r *RechargeController) SetRoom() {
	var room models.Room
	err := json.Unmarshal(r.Ctx.Input.RequestBody, &room)
	beego.Info("json data = ", room)

	base, _ := beego.AppConfig.Int("baseNum")
	if err == nil && (int(room.UserId) > base) && room.Person > 0 && room.Round > 0 {
		if (room.Person < 4 && room.Poker == 0) || (room.Person == 4 && room.Poker < 2) || (room.Person == 5 && room.Poker == 1) || (room.Person == 6 && room.Poker == 2) {
			base, _ := beego.AppConfig.Int("baseNum")
			userId := room.UserId - int32(base)

			o := orm.NewOrm()
			num, err := o.QueryTable(new(models.TLogRoomSet)).Filter("uin", uint(userId)).Filter("person", uint(room.Person)).Filter("round", uint(room.Round)).Filter("subPoker", uint(room.Poker)).Filter("payAll", room.PayAll).Filter("status", 0).Count()

			if err == nil && num == 0 {
				for k, v := range room.Power {
					acount := models.TAccount{Uin: uint(v.GameUserId - int32(base))}
					o.Read(&acount)
					room.Power[k].GameName = acount.Nick
				}

				pInfo, _ := json.Marshal(room.Power)
				t := time.Now()
				oid := "setroom" + strconv.FormatInt(int64(userId), 10) + strconv.FormatInt(t.UnixNano(), 10)
				tLogRoomSet := models.TLogRoomSet{
					GPoker: 	bool(room.GPoker),
					MPoker:		bool(room.MPoker),
					Multiple:	uint(room.Multiple),
					Uin:        uint(userId),
					Agent:      uint(room.Agent),
					Round:      uint(room.Round),
					SubPoker:   uint(room.Poker),
					PayAll:     room.PayAll,
					Person:     uint(room.Person),
					PlayerInfo: string(pInfo),
					Oid:        oid,
					CreateTime: t,
				}
				//iId,_ := o.Insert(&tLogRoomSet)

				var players []*msg.Player
				for _, v := range room.Power {
					player := &msg.Player{
						GameUserId: v.GameUserId,
						GoodCard:   v.GoodCard,
					}
					players = append(players, player)
				}
				game := &msg.MsgWinGame{
					GPoker: 	room.GPoker,
					MPoker:		room.MPoker,
					Multiple:	msg.GAME_MULTI(room.Multiple),
					UserId: room.UserId,
					Person: room.Person,
					Round:  room.Round,
					Poker:  room.Poker,
					PayAll: room.PayAll,
					Power:  players,
					Oid:    oid,
				}
				data, _ := proto.Marshal(game)

				ws := webSocket(data, 10014)
				if ws {
					//tLogRoomSet.Id = uint64(iId)
					//tLogRoomSet.Status = 1
					tLogRoomSet.UpdateTime = t
					_, err := o.Insert(&tLogRoomSet)
					//_, err := o.Update(&tLogRoomSet, "roomNo", "status", "updateTime")
					if err == nil {
						data := models.RoomInfo{
							Code: 200,
							Msg:  "房间玩家胜场设置成功",
						}
						r.Data["json"] = data
					} else {
						beego.Error("room set err = ", err)
						data := models.RoomInfo{
							Code: 500,
							Msg:  "创建数据失败",
						}
						r.Data["json"] = data
					}
				} else {
					data := models.RoomInfo{
						Code: 500,
						Msg:  "创建失败。",
					}
					r.Data["json"] = data
				}
			} else {
				data := models.RoomInfo{
					Code: 500,
					Msg:  "已创建，请删除重试。",
				}
				r.Data["json"] = data
			}
		} else {
			data := models.RoomInfo{
				Code: 500,
				Msg:  "房间设置不正确",
			}
			r.Data["json"] = data
		}
	} else {
		data := models.RoomInfo{
			Code: 500,
			Msg:  "数据格式不正确或者用户ID不正确",
		}
		r.Data["json"] = data
	}

	r.ServeJSON()
}

// @Title CreateOrder
// @Description create order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /roomList [post]
func (r *RechargeController) RoomList() {
	var roomList models.RoomList
	var msgInfo models.MsgInfo
	err := json.Unmarshal(r.Ctx.Input.RequestBody, &roomList)
	beego.Info("json data = ", roomList)

	if err == nil && roomList.Page > 0 && roomList.PageSize > 0 {
		base, _ := beego.AppConfig.Int("baseNum")
		start := (roomList.Page - 1) * roomList.PageSize
		size := roomList.PageSize

		var tLogRoomSet []models.TLogRoomSet
		var ct int64
		var err error
		o := orm.NewOrm()

		if roomList.Agent > 0 && roomList.UserId > 0 {
			_, err = o.QueryTable(new(models.TLogRoomSet)).Filter("status", 0).Filter("agent", roomList.Agent).Filter("uin", roomList.UserId-uint(base)).Limit(size, start).OrderBy("-create_time").All(&tLogRoomSet)
			ct, _ = o.QueryTable(new(models.TLogRoomSet)).Filter("status", 0).Filter("agent", roomList.Agent).Filter("uin", roomList.UserId-uint(base)).Limit(-1).Count()
		}

		if roomList.Agent > 0 {
			_, err = o.QueryTable(new(models.TLogRoomSet)).Filter("status", 0).Filter("agent", roomList.Agent).Limit(size, start).OrderBy("-create_time").All(&tLogRoomSet)
			ct, _ = o.QueryTable(new(models.TLogRoomSet)).Filter("status", 0).Filter("agent", roomList.Agent).Limit(-1).Count()
		}

		if err == nil && math.Ceil(float64(ct)/float64(size)) >= float64(roomList.Page) {
			var rooms []models.Room
			for _, v := range tLogRoomSet {
				var player []models.Player
				err := json.Unmarshal([]byte(v.PlayerInfo), &player)
				if err == nil {
					room := &models.Room{
						GPoker: 	bool(v.GPoker),
						MPoker:		bool(v.MPoker),
						Multiple:	int32(v.Multiple),
						Agent:      int32(v.Agent),
						UserId:     int32(v.Uin) + int32(base),
						Person:     int32(v.Person),
						Round:      int32(v.Round),
						Poker:      int32(v.SubPoker),
						PayAll:     v.PayAll,
						Power:      player,
						Oid:        v.Oid,
						CreateTime: v.CreateTime,
						Status:     int32(v.Status),
					}
					rooms = append(rooms, *room)
				} else {
					beego.Error("RoomList: json err = ", err)
				}
			}
			beego.Error("RoomList: rooms = ", rooms, ct)
			data := models.RoomData{
				Code: 200,
				Data: rooms,
				Ct:   ct,
			}
			r.Data["json"] = data
		} else {
			beego.Error("RoomList: err = ", err)
			msgInfo.Code = 500
			msgInfo.Msg = "查询错误"
			r.Data["json"] = msgInfo
		}
	} else {
		msgInfo.Code = 500
		msgInfo.Msg = "数据格式有误"
		r.Data["json"] = msgInfo
	}
	r.ServeJSON()
}

// @Title CreateOrder
// @Description create order
// @Param	body		body 	models.User	true		"body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router /delroom [post]
func (r *RechargeController) DelRoom() {
	var jData map[string]string
	var delMsg models.MsgInfo
	err := json.Unmarshal(r.Ctx.Input.RequestBody, &jData)
	beego.Info("json data = ", jData)

	if err == nil {
		o := orm.NewOrm()
		var roomSet models.TLogRoomSet
		//err := o.Read(&roomSet)
		err := o.QueryTable(new(models.TLogRoomSet)).Filter("oid", jData["oid"]).One(&roomSet)
		if err == nil {
			base, _ := beego.AppConfig.Int("baseNum")
			wg := &msg.MsgDelWinGame{
				Master: int32(roomSet.Uin) + int32(base),
				Oid:    roomSet.Oid,
			}
			data, _ := proto.Marshal(wg)

			ws := webSocket(data, 10016)
			if ws {
				_, err := o.QueryTable(new(models.TLogRoomSet)).Filter("oid", jData["oid"]).Update(orm.Params{
					"status":     1,
					"updateTime": time.Now(),
				})
				if err != nil {
					beego.Info("update err = ", err)
					delMsg.Code = 500
					delMsg.Msg = "更新失败"
					r.Data["json"] = delMsg
				} else {
					delMsg.Code = 200
					delMsg.Msg = "删除成功"
					r.Data["json"] = delMsg
				}
			} else {
				delMsg.Code = 500
				delMsg.Msg = "数据删除失败"
				r.Data["json"] = delMsg
			}
		} else {
			beego.Info("json err = ", err)
			delMsg.Code = 500
			delMsg.Msg = "记录不存在"
			r.Data["json"] = delMsg
		}
	} else {
		beego.Info("json err = ", err)
		delMsg.Code = 500
		delMsg.Msg = "数据格式不正确"
		r.Data["json"] = delMsg
	}

	r.ServeJSON()
}

func webSocket(data []byte, msgId uint32) bool {
WS:
	m := make([]byte, 4+len(data))

	// 默认使用大端序
	binary.BigEndian.PutUint32(m, msgId)

	copy(m[4:], data)
	err := conn.WriteMessage(websocket.TextMessage, m)
	if err != nil {
		beego.Info("webSocket: write = ", err)
		initConn()
		goto WS
	} else {
		beego.Info("发送成功", msgId)

		_, message, err := conn.ReadMessage()
		if err != nil {
			beego.Info("webSocket: recive err = ", err)
			initConn()
			goto WS
		} else {
			msgPong := &msg.MsgError{}
			err = proto.Unmarshal(message[4:], msgPong)
			beego.Info("webSocket: recive = ", msgPong.GetDesc())
			if msgPong.GetDesc() == "pong" {
				return true
			}
		}
	}
	return false
}
